package top.hymsk.ecommerce.dao.elasticsearch.impl;

import com.alibaba.fastjson.JSON;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.springframework.stereotype.Repository;
import top.hymsk.ecommerce.anno.RedisCache;
import top.hymsk.ecommerce.dao.elasticsearch.ESConnection;
import top.hymsk.ecommerce.dao.elasticsearch.ElasticSearchMapper;
import top.hymsk.ecommerce.dao.mapper.ProductMapper;
import top.hymsk.ecommerce.pojo.DTO.BaseProductDTO;
import top.hymsk.ecommerce.pojo.DTO.BuyerProductDTO;
import top.hymsk.ecommerce.pojo.DTO.ProductResponseDTO;
import top.hymsk.ecommerce.pojo.PO.es.ElasticSearchProductPO;
import top.hymsk.ecommerce.pojo.status.ProductStatusCode;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 花于陌上开
 */
@Repository
public class ElasticSearchMapperImpl implements ElasticSearchMapper {
    final static String INDEX_NAME = "shopping";
    final ESConnection esConnection;
    final ProductMapper productMapper;

    public ElasticSearchMapperImpl(ESConnection esConnection, ProductMapper productMapper) {
        this.esConnection = esConnection;
        this.productMapper = productMapper;
    }

    /**
     * 查询商品详情
     *
     * @param productId 商品id
     * @return BaseProductDTO
     */
    @Override
    @RedisCache
    public BuyerProductDTO searchProduct(Integer productId) throws IOException {
        try (RestHighLevelClient client = esConnection.getClient()) {
            GetRequest request = new GetRequest(INDEX_NAME, String.valueOf(productId));
            GetResponse response = client.get(request, RequestOptions.DEFAULT);
            String json = response.getSourceAsString();
            return JSON.parseObject(json, BuyerProductDTO.class);
        }
    }

    /**
     * 获取默认推荐结果
     *
     * @param page 分页参数
     * @return ProductResponseDTO
     */
    @Override
    @RedisCache
    public ProductResponseDTO search(Integer page) throws Exception {
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
                QueryBuilders.matchAllQuery(),
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                ScoreFunctionBuilders.fieldValueFactorFunction("star")  //算分函数
                        ), new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("ad_status", "true"),
                        ScoreFunctionBuilders.fieldValueFactorFunction("ad_weight") //算分函数
                )}).boostMode(CombineFunction.MULTIPLY);
        return originSearch(functionScoreQueryBuilder, page);
    }

    /**
     * 查询
     *
     * @param keyword 查询词
     * @param page    分页参数
     * @return ProductResponseDTO
     */
    @Override
    public ProductResponseDTO searchByKeyword(String keyword, Integer page) throws Exception {
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(
                QueryBuilders.matchQuery("all", keyword),
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                ScoreFunctionBuilders.fieldValueFactorFunction("star")  //算分函数
                        ), new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("ad_status", true),
                        ScoreFunctionBuilders.fieldValueFactorFunction("ad_weight") //算分函数
                )}).boostMode(CombineFunction.SUM);
        return originSearch(functionScoreQueryBuilder, page);
    }

    /**
     * 添加商品到ES
     *
     * @param productId 商品id
     * @return 成功/失败
     */
    @Override
    public Boolean pushProductFromDatabase(Integer productId) {
        ElasticSearchProductPO product = productMapper.getElasticSearchProduct(productId);
        double weight=0,status=product.getStatus();
        String adStatus="false";
        if(status>=2){
            adStatus="true";
            if (status==2){
                weight= ProductStatusCode.ON_SALE_VIP.getWeight();
            }
            else if (status==3){
                weight= ProductStatusCode.ON_SALE_SUPER_VIP.getWeight();
            }
            else if (status==4){
                weight= ProductStatusCode.ON_SALE_SUPREMACY_VIP.getWeight();
            }
        }
        deleteProduct(productId);
        Double star=product.getStar();
        String jsonDocument = "{\n" + "  \"productId\": " + productId +
                ",\n" + "  \"title\":\"" + product.getTitle().replaceAll("\\s", " ").replaceAll("\\\"|\\\\", "\\\\$0")
                + "\",\n" + "  \"price\":\"" + product.getPrice()
                + "\",\n" + "  \"tag\":\"" + product.getTag()
                + "\",\n" + "  \"storeName\":\"" + product.getStoreName().replaceAll("\\s", " ").replaceAll("\\\"|\\\\", "\\\\$0")
                + "\",\n" + "  \"filePath\":\"" + product.getFilePath()
                + "\",\n" + "  \"star\":\"" + (star == null ? 2.5 : star)+ "\",\n"
                + "  \"ad_status\":"+adStatus+",\n" + "  \"ad_weight\":"+weight+"\n" + "}";
        try (RestHighLevelClient client = esConnection.getClient()) {
            IndexRequest request = new IndexRequest(INDEX_NAME).id(String.valueOf(productId));
            request.source(jsonDocument, XContentType.JSON);
            client.index(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除商品
     *
     * @param productId 商品id
     * @return 成功/失败
     */
    @Override
    public Boolean deleteProduct(Integer productId) {
        try (RestHighLevelClient client = esConnection.getClient()) {
            DeleteRequest request = new DeleteRequest(INDEX_NAME).id(String.valueOf(productId));
            client.delete(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            return false;
        }
        return true;
    }


    public ProductResponseDTO originSearch(FunctionScoreQueryBuilder functionScoreQueryBuilder, Integer page) throws Exception {
        if (page > (ESConnection.MAX_RESULT_NUMBER / ESConnection.PAGE_SIZE)) {
            throw new Exception("非法请求");
        }
        page=page*ESConnection.PAGE_SIZE;
        List<BuyerProductDTO> productList = new ArrayList<>();
        long total;
        try (RestHighLevelClient client = esConnection.getClient()) {
            SearchRequest request = new SearchRequest(INDEX_NAME);
            request.source().query(functionScoreQueryBuilder);
            request.source().from(page).size(ESConnection.PAGE_SIZE);
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            SearchHits searchHits = response.getHits();
            SearchHit[] hits = searchHits.getHits();
            total = searchHits.getTotalHits().value;
            if (total > ESConnection.MAX_RESULT_NUMBER) {
                total = ESConnection.MAX_RESULT_NUMBER;
            }
            for (SearchHit hit : hits) {
                String json = hit.getSourceAsString();
                productList.add(JSON.parseObject(json, BuyerProductDTO.class));
//                System.out.println(hit.getScore()+" "+json);
            }
        }
        return new ProductResponseDTO(productList, total);
    }
}
